#include <windows.h>
#include <stdio.h>
#include "api.h"
#include "image.h"

#pragma pack(1)

typedef struct _BMPREADERHANDLER_ *PBMPREADERHANDLER;

typedef void (*LINE_PARSER_CALLBACK)(PBMPREADERHANDLER data, PBYTE input, PBYTE output);

typedef struct _BMPREADERHANDLER_{
	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER infoHeader;
	FILE *fbmp;
	PRGBENTRY colors;
	PBYTE line;
	int lineLen;
	LINE_PARSER_CALLBACK lineParser;
}BMPREADERHANDLER;

DWORD bmpReaderCreateHandler(){
	NEW_DATA(BMPREADERHANDLER);
	return (DWORD)data;
}

void bmpReaderParseNone(PBMPREADERHANDLER data, PBYTE input, PBYTE output){
	memset(output, 0xFF, data->infoHeader.biWidth*3);
}

void bmpReaderParse24Bits(PBMPREADERHANDLER data, PBYTE input, PBYTE output){
	memcpy(output, input, data->lineLen);
}

void bmpReaderParse8Bits(PBMPREADERHANDLER data, PBYTE input, PBYTE output){
	int i;
	for(i=0;i<data->infoHeader.biWidth;i++){
		*output++ = data->colors[*input].r;
		*output++ = data->colors[*input].g;
		*output++ = data->colors[*input++].b;
	}
}

void bmpReaderParse4Bits(PBMPREADERHANDLER data, PBYTE input, PBYTE output){
	int i;
	int icolor;
	for(i=0;i<data->infoHeader.biWidth;i++){
		if (i&1){
			icolor = (*input)&0x0F;
			input++;
		}
		else{
			icolor = (*input)>>4;
		}
		*output++ = data->colors[icolor].r;
		*output++ = data->colors[icolor].g;
		*output++ = data->colors[icolor].b;
	}
}

void bmpReaderLoadHeader(PBMPREADERHANDLER data, PIMAGEHEADER header){
	int colors;
	fread(&data->fileHeader, sizeof(BITMAPFILEHEADER), 1, data->fbmp);
	fread(&data->infoHeader, sizeof(BITMAPINFOHEADER), 1, data->fbmp);
	header->bits = data->infoHeader.biBitCount;
	header->dx = data->infoHeader.biWidth;
	header->dy = data->infoHeader.biHeight;
	colors = 1 << header->bits;
	if (header->bits<24){	// Need read the palette info
		
		data->colors = (PRGBENTRY)apiNew(sizeof(RGBENTRY)*colors);
		fread(data->colors, sizeof(RGBENTRY), colors, data->fbmp);
	}
	header->flg = 0;	// Sequential reading
	data->lineLen = (((header->bits*header->dx + 31) & (~31))>>3);
	switch(header->bits){
	case 24:
		data->lineParser = bmpReaderParse24Bits;
		break;
	case 8:
		data->lineParser = bmpReaderParse8Bits;
		break;
	case 4:
		data->lineParser = bmpReaderParse4Bits;
		break;
	default:
		data->lineParser = bmpReaderParseNone;
		break;
	}
	data->line = (PBYTE)malloc(data->lineLen);
}

void bmpReaderScanLine(PBMPREADERHANDLER data, PBYTE output){
	fread(data->line, data->lineLen, 1, data->fbmp);
	data->lineParser(data, data->line, output);
}

void bmpReaderDeleteHandler(PBMPREADERHANDLER data){
	if (data->colors){
		free(data->colors);
	}
	free(data->line);
	FREE_DATA();
}

DWORD bmpReader(UINT command, PVOID handler, PVOID param){
	PBMPREADERHANDLER data = (PBMPREADERHANDLER)handler;
	switch(command){
	case IMAGE_LOADER_CREATEHANDLER:
		return bmpReaderCreateHandler();
	case IMAGE_LOADER_OPENIMAGE:
		data->fbmp = fopen((char*)param, "rb+");
		break;
	case IMAGE_LOADER_LOADHEADER:
		bmpReaderLoadHeader(data, (PIMAGEHEADER)param);
		break;
	case IMAGE_LOADER_SCANLINE:
		bmpReaderScanLine(data, (PBYTE)param);
		break;
	case IMAGE_LOADER_CLOSEIMAGE:
		fclose(data->fbmp);
		break;
	case IMAGE_LOADER_DELETEHANDLER:
		bmpReaderDeleteHandler(data);
		break;
	}
	return 0;
}